<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="springspring-web">
      Spring源码解析系列：Spring Web 请求初探
     </h1>
     <hr/>
     <h2 id="_1">
      简介
     </h2>
     <p>
      这几年工作中大部分时间都与SpringWeb打交道，前几年源码阅读能力和方法论不行，在疫情期间学习了一波，有了源码阅读与分析能力，目前也有了一些闲暇时间，以目前自己的水平去写写Spring相关的源码解析
     </p>
     <p>
      本篇文章就先简单热个身，看看一个请求是如何处理到达我们平时写的Controllers里面的
     </p>
     <h2 id="_2">
      准备工作
     </h2>
     <p>
      准备工作较为简单，我们使用
      <a href="https://start.spring.io/">
       spring initializr
      </a>
      ,搭建一个初始工程，写一个简单的HelloWorld
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.GetMapping</span><span class="p">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="p">;</span>

<span class="nd">@RestController</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="p">{</span>

    <span class="nd">@GetMapping</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
    <span class="kd">public</span> <span class="n">String</span> <span class="nf">helloWorld</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="s">"Hello world"</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      推荐IDEA上一个比较好用的插件：RestfulTool和RestfulToolKit
     </p>
     <p>
      启动项目后，会读取所有的请求列表，简单快捷的发起一个请求：
     </p>
     <p>
      <img alt="IDEA推荐SpringWeb插件.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0797804ce8847d598ed710345aa3c70~tplv-k3u1fbpfcp-watermark.image"/>
     </p>
     <p>
      这样，本章的初步环境就搭建好了，下面开始源码分析：
     </p>
     <h2 id="_3">
      源码解析
     </h2>
     <p>
      我们直接在新增的HelloWorld类的：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="k">return</span> <span class="s">"Hello world"</span><span class="p">;</span> 
</code></pre>
     </div>
     <p>
      这行打断点，直接发起请求，可以看到下面的调用栈：
     </p>
     <p>
      <img alt="调试调用栈.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3ee8b661927549cc97369f4eea940f66~tplv-k3u1fbpfcp-watermark.image"/>
     </p>
     <p>
      我们从下网上，大体的点击进去，看看大意
     </p>
     <p>
      带着问题去看：
     </p>
     <ul>
      <li>
       从那接收的请求：如何写过相关网络编程的，那就会想到起码有一个Netty之类的服务，监听在指定端口，接收请求
      </li>
      <li>
       接收到的请求如何找到我们写的页面代码（HelloWorld）：比如如何将请求路径和处理函数进行对应
      </li>
     </ul>
     <p>
      一个简单的本地探索的请求如下：
     </p>
     <p>
      <img alt="Springweb初探.png" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4687e652cc7f41b5bd6a6be4dfe43511~tplv-k3u1fbpfcp-watermark.image"/>
     </p>
     <h3 id="_4">
      找到大致的监听入口
     </h3>
     <p>
      我们从下往上看是，发现有一个类：NioEndpoint.java,虽然看不太懂更多的细节，但知道有这么一个东西即可
     </p>
     <h3 id="requestresponse">
      找到重要的Request和Response处理函数
     </h3>
     <p>
      我们继续来到一个重要的类：Http11Processor.java，其中有添加Filter相关的代码，还是明显的Request和Response的处理
     </p>
     <p>
      这里我们留下两个疑问，目前我们先把请求路径梳理下，这些细节日后我们再回过头来啃它
     </p>
     <ul>
      <li>
       这个类中设置的Filter如何使用
      </li>
      <li>
       Request和Response是如何转化得到的
      </li>
     </ul>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Http11Processor</span> <span class="kd">extends</span> <span class="n">AbstractProcessor</span> <span class="p">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="n">SocketState</span> <span class="nf">service</span><span class="p">(</span><span class="n">SocketWrapperBase</span><span class="o">&lt;?&gt;</span> <span class="n">socketWrapper</span><span class="p">)</span>
        <span class="kd">throws</span> <span class="n">IOException</span> <span class="p">{</span>
        <span class="n">RequestInfo</span> <span class="n">rp</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="na">getRequestProcessor</span><span class="p">();</span>
        <span class="n">rp</span><span class="p">.</span><span class="na">setStage</span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="na">apache</span><span class="p">.</span><span class="na">coyote</span><span class="p">.</span><span class="na">Constants</span><span class="p">.</span><span class="na">STAGE_PARSE</span><span class="p">);</span>

        <span class="c1">// Setting up the I/O</span>
        <span class="n">setSocketWrapper</span><span class="p">(</span><span class="n">socketWrapper</span><span class="p">);</span>

        <span class="c1">// Flags</span>
        <span class="n">keepAlive</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
        <span class="n">openSocket</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
        <span class="n">readComplete</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
        <span class="kt">boolean</span> <span class="n">keptAlive</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
        <span class="n">SendfileState</span> <span class="n">sendfileState</span> <span class="o">=</span> <span class="n">SendfileState</span><span class="p">.</span><span class="na">DONE</span><span class="p">;</span>

        <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">getErrorState</span><span class="p">().</span><span class="na">isError</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">keepAlive</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isAsync</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">upgradeToken</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span>
                <span class="n">sendfileState</span> <span class="o">==</span> <span class="n">SendfileState</span><span class="p">.</span><span class="na">DONE</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">protocol</span><span class="p">.</span><span class="na">isPaused</span><span class="p">())</span> <span class="p">{</span>

            <span class="c1">// Parsing the request header</span>
            <span class="k">try</span> <span class="p">{</span>
        <span class="p">......</span>
            <span class="p">}</span>

            <span class="c1">// Has an upgrade been requested?</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">isConnectionToken</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="na">getMimeHeaders</span><span class="p">(),</span> <span class="s">"upgrade"</span><span class="p">))</span> <span class="p">{</span>
                <span class="p">......</span>
            <span class="p">}</span>
        <span class="p">......</span>

            <span class="c1">// Process the request in the adapter</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">getErrorState</span><span class="p">().</span><span class="na">isIoAllowed</span><span class="p">())</span> <span class="p">{</span>
                <span class="k">try</span> <span class="p">{</span>
                    <span class="n">rp</span><span class="p">.</span><span class="na">setStage</span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="na">apache</span><span class="p">.</span><span class="na">coyote</span><span class="p">.</span><span class="na">Constants</span><span class="p">.</span><span class="na">STAGE_SERVICE</span><span class="p">);</span>
                    <span class="n">getAdapter</span><span class="p">().</span><span class="na">service</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">response</span><span class="p">);</span>
                    <span class="c1">// Handle when the response was committed before a serious</span>
                    <span class="c1">// error occurred.  Throwing a ServletException should both</span>
                    <span class="c1">// set the status to 500 and set the errorException.</span>
                    <span class="c1">// If we fail here, then the response is likely already</span>
                    <span class="c1">// committed, so we can't try and set headers.</span>
                    <span class="k">if</span><span class="p">(</span><span class="n">keepAlive</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">getErrorState</span><span class="p">().</span><span class="na">isError</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isAsync</span><span class="p">()</span> <span class="o">&amp;&amp;</span>
                            <span class="n">statusDropsConnection</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="na">getStatus</span><span class="p">()))</span> <span class="p">{</span>
                        <span class="n">setErrorState</span><span class="p">(</span><span class="n">ErrorState</span><span class="p">.</span><span class="na">CLOSE_CLEAN</span><span class="p">,</span> <span class="kc">null</span><span class="p">);</span>
                    <span class="p">}</span>
                <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">InterruptedIOException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">setErrorState</span><span class="p">(</span><span class="n">ErrorState</span><span class="p">.</span><span class="na">CLOSE_CONNECTION_NOW</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
                <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">HeadersTooLargeException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">sm</span><span class="p">.</span><span class="na">getString</span><span class="p">(</span><span class="s">"http11processor.request.process"</span><span class="p">),</span> <span class="n">e</span><span class="p">);</span>
                    <span class="c1">// The response should not have been committed but check it</span>
                    <span class="c1">// anyway to be safe</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="na">isCommitted</span><span class="p">())</span> <span class="p">{</span>
                        <span class="n">setErrorState</span><span class="p">(</span><span class="n">ErrorState</span><span class="p">.</span><span class="na">CLOSE_NOW</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
                    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                        <span class="n">response</span><span class="p">.</span><span class="na">reset</span><span class="p">();</span>
                        <span class="n">response</span><span class="p">.</span><span class="na">setStatus</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
                        <span class="n">setErrorState</span><span class="p">(</span><span class="n">ErrorState</span><span class="p">.</span><span class="na">CLOSE_CLEAN</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
                        <span class="n">response</span><span class="p">.</span><span class="na">setHeader</span><span class="p">(</span><span class="s">"Connection"</span><span class="p">,</span> <span class="s">"close"</span><span class="p">);</span> <span class="c1">// TODO: Remove</span>
                    <span class="p">}</span>
                <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">Throwable</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">ExceptionUtils</span><span class="p">.</span><span class="na">handleThrowable</span><span class="p">(</span><span class="n">t</span><span class="p">);</span>
                    <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">sm</span><span class="p">.</span><span class="na">getString</span><span class="p">(</span><span class="s">"http11processor.request.process"</span><span class="p">),</span> <span class="n">t</span><span class="p">);</span>
                    <span class="c1">// 500 - Internal Server Error</span>
                    <span class="n">response</span><span class="p">.</span><span class="na">setStatus</span><span class="p">(</span><span class="mi">500</span><span class="p">);</span>
                    <span class="n">setErrorState</span><span class="p">(</span><span class="n">ErrorState</span><span class="p">.</span><span class="na">CLOSE_CLEAN</span><span class="p">,</span> <span class="n">t</span><span class="p">);</span>
                    <span class="n">getAdapter</span><span class="p">().</span><span class="na">log</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">......</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <h3 id="valva">
      经过了一系列的Valva类处理
     </h3>
     <p>
      继续往上查看，我们看到了很多的Value相关的处理，类型一种责任链的处理方式，但目前还不知道其具体作用，但没关系，我们继续往下看
     </p>
     <h3 id="filter">
      经过了一系列的Filter处理
     </h3>
     <p>
      经过上面那些Valva类的处理后，来到了我们熟悉的Filter处理，具体细节我们先不深究，继续往下，留下疑问待日后处理
     </p>
     <ul>
      <li>
       Filter如何初始化的
      </li>
      <li>
       如何针对指定的请求路径进行处理
      </li>
      <li>
       自定义添加的Filter会插入其中吗？如何插入？
      </li>
     </ul>
     <h3 id="_5">
      请求路径到具体函数处理
     </h3>
     <p>
      继续走，我们来到属性的类：DispatcherServlet.java，平时看文章之类的，这个类的出现率还是挺高的
     </p>
     <p>
      通过断点调试，我们发现在这里直接得到了我们请求路径对应的请求方法，如下代码中标注的：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DispatcherServlet</span> <span class="p">{</span>
    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doDispatch</span><span class="p">(</span><span class="n">HttpServletRequest</span> <span class="n">request</span><span class="p">,</span> <span class="n">HttpServletResponse</span> <span class="n">response</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="p">{</span>
    <span class="n">HttpServletRequest</span> <span class="n">processedRequest</span> <span class="o">=</span> <span class="n">request</span><span class="p">;</span>
    <span class="n">HandlerExecutionChain</span> <span class="n">mappedHandler</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
    <span class="kt">boolean</span> <span class="n">multipartRequestParsed</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>

    <span class="n">WebAsyncManager</span> <span class="n">asyncManager</span> <span class="o">=</span> <span class="n">WebAsyncUtils</span><span class="p">.</span><span class="na">getAsyncManager</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>

    <span class="k">try</span> <span class="p">{</span>
        <span class="n">ModelAndView</span> <span class="n">mv</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
        <span class="n">Exception</span> <span class="n">dispatchException</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>

        <span class="k">try</span> <span class="p">{</span>
            <span class="n">processedRequest</span> <span class="o">=</span> <span class="n">checkMultipart</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>
            <span class="n">multipartRequestParsed</span> <span class="o">=</span> <span class="p">(</span><span class="n">processedRequest</span> <span class="o">!=</span> <span class="n">request</span><span class="p">);</span>

            <span class="c1">// Determine handler for the current request.</span>
            <span class="c1">// 得到请求的处理方法</span>
            <span class="n">mappedHandler</span> <span class="o">=</span> <span class="n">getHandler</span><span class="p">(</span><span class="n">processedRequest</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">mappedHandler</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">noHandlerFound</span><span class="p">(</span><span class="n">processedRequest</span><span class="p">,</span> <span class="n">response</span><span class="p">);</span>
                <span class="k">return</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mappedHandler</span><span class="p">.</span><span class="na">applyPreHandle</span><span class="p">(</span><span class="n">processedRequest</span><span class="p">,</span> <span class="n">response</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">return</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="c1">// Actually invoke the handler.</span>
            <span class="c1">// 这行通过断点查看：mappedHandler就是我们请求的处理方法</span>
            <span class="n">mv</span> <span class="o">=</span> <span class="n">ha</span><span class="p">.</span><span class="na">handle</span><span class="p">(</span><span class="n">processedRequest</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">mappedHandler</span><span class="p">.</span><span class="na">getHandler</span><span class="p">());</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">asyncManager</span><span class="p">.</span><span class="na">isConcurrentHandlingStarted</span><span class="p">())</span> <span class="p">{</span>
                <span class="k">return</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="n">applyDefaultViewName</span><span class="p">(</span><span class="n">processedRequest</span><span class="p">,</span> <span class="n">mv</span><span class="p">);</span>
            <span class="n">mappedHandler</span><span class="p">.</span><span class="na">applyPostHandle</span><span class="p">(</span><span class="n">processedRequest</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">mv</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span> <span class="p">{</span>
            <span class="p">........</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      我们继续往下，来到了一个类：InvocableHandlerMethod.java，其中的：method(getBean(), args)，相比很熟悉了，经典的反射调用
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">InvocableHandlerMethod</span> <span class="p">{</span>
    <span class="kd">protected</span> <span class="n">Object</span> <span class="nf">doInvoke</span><span class="p">(</span><span class="n">Object</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="p">{</span>
        <span class="n">Method</span> <span class="n">method</span> <span class="o">=</span> <span class="n">getBridgedMethod</span><span class="p">();</span>
        <span class="n">ReflectionUtils</span><span class="p">.</span><span class="na">makeAccessible</span><span class="p">(</span><span class="n">method</span><span class="p">);</span>
        <span class="k">try</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">KotlinDetector</span><span class="p">.</span><span class="na">isSuspendingFunction</span><span class="p">(</span><span class="n">method</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">return</span> <span class="n">CoroutinesUtils</span><span class="p">.</span><span class="na">invokeSuspendingFunction</span><span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">getBean</span><span class="p">(),</span> <span class="n">args</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="n">method</span><span class="p">.</span><span class="na">invoke</span><span class="p">(</span><span class="n">getBean</span><span class="p">(),</span> <span class="n">args</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">IllegalArgumentException</span> <span class="n">ex</span><span class="p">)</span> <span class="p">{</span>
            <span class="p">......</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      在这里我们也留下疑问：
     </p>
     <ul>
      <li>
       如何得到请求路径对应的处理方法？
      </li>
      <li>
       如何解析获取处理方法的参数的？
      </li>
     </ul>
     <h2 id="_6">
      总结
     </h2>
     <p>
      经过上面的简单分析，我们大致得到了一个请求的处理路径：
     </p>
     <ul>
      <li>
       NioEndpoint.java : 服务监听
      </li>
      <li>
       Http11Processor.java : 请求与响应处理入口
      </li>
      <li>
       一系列的Valva类处理
      </li>
      <li>
       一系列的Filter处理
      </li>
      <li>
       DispatcherServlet.java : 请求路径到具体处理方法
      </li>
      <li>
       InvocableHandlerMethod.java : 反射调用处理
      </li>
     </ul>
     <p>
      当前我们拿到一个比较大的地图，但很多地方还被迷雾笼罩，接下来我们慢慢去除迷雾，探索其奥秘
     </p>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
